package com.uuah.server.endpoint;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.UuahServer;
import com.uuah.server.api.endpoint.IEndpointURI;
import com.uuah.server.api.endpoint.IEndpointURIBuilder;
import com.uuah.server.api.transport.service.ITransportServiceDescriptor;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.config.registry.ServiceDescriptorFactory;
import com.uuah.server.exception.endpoint.EndpointException;
import com.uuah.server.exception.endpoint.MalformedEndpointException;
import com.uuah.server.exception.registry.ServiceException;
import com.uuah.utils.ClassUtils;
import com.uuah.utils.PropertiesUtils;
import com.uuah.utils.StringUtils;

/**
 * <code>UuahEndpointURI</code> is used to determine how a message is sent of
 * received. The url defines the protocol, the endpointUri destination of the
 * message and optionally the endpoint to use when dispatching the event. Uuah App Server
 * urls take the form of - protocol://[host]:[port]/[provider]/endpointUri or
 * protocol://[host]:[port]/endpointUri i.e. vm:///my.object or The protocol can
 * be any of any connector registered with Uuah App Server. The endpoint name if specified
 * must be the name of a register global endpoint The endpointUri can be any
 * endpointUri recognised by the endpoint type.
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午01:37:35
 * @version 1.0.0
 */
public class UuahEndpointURI implements IEndpointURI {

	private static final long serialVersionUID = 3906735768171252877L;

	protected transient final Logger logger = LoggerFactory
			.getLogger(UuahEndpointURI.class);

	public static boolean isUuahUri(String url) {
		return url.indexOf(":/") != -1;
	}

	private String address;
	private String filterAddress;
	private String endpointName;
	private String connectorName;
	private String requestTransformers;
	private String responseTransformers;
	private Properties params = new Properties();
	private URI uri;
	private String userInfo;
	private String schemeMetaInfo;

	public UuahEndpointURI(String address, String endpointName,
			String connectorName, String transformers,
			String responseTransformers, Properties properties, URI uri,
			String userInfo) {
		this(address, endpointName, connectorName, transformers,
				responseTransformers, properties, uri);
		if (userInfo != null) {
			this.userInfo = userInfo;
		}
	}

	public UuahEndpointURI(String address, String endpointName,
			String connectorName, String requestTransformers,
			String responseTransformers, Properties properties, URI uri) {
		this.address = address;
		this.endpointName = endpointName;
		this.connectorName = connectorName;
		this.requestTransformers = requestTransformers;
		this.responseTransformers = responseTransformers;
		this.params = properties;
		this.uri = uri;
		this.userInfo = uri.getUserInfo();

	}

	public UuahEndpointURI(IEndpointURI endpointUri) {
		initialise(endpointUri);
	}

	public UuahEndpointURI(IEndpointURI endpointUri, String filterAddress) {
		initialise(endpointUri);
		this.filterAddress = filterAddress;
	}

	public UuahEndpointURI(String uri) throws EndpointException {
		uri = uri.trim().replaceAll(" ", "%20");
		uri = uri.replaceAll("\\{", "\\[");
		uri = uri.replaceAll("\\}", "\\]");

		if (!validateUrl(uri)) {
			throw new MalformedEndpointException(uri);
		}
		try {
			schemeMetaInfo = retrieveSchemeMetaInfo(uri);
			if (schemeMetaInfo != null) {
				uri = uri.replaceFirst(schemeMetaInfo + ":", "");
			}
			this.uri = new URI(uri);
			this.userInfo = this.uri.getRawUserInfo();
		} catch (URISyntaxException e) {
			throw new MalformedEndpointException(uri, e);
		}
	}

	public void initialise() throws InitialisationException {
		try {
			String scheme = (schemeMetaInfo == null ? this.uri.getScheme()
					: schemeMetaInfo);
			ITransportServiceDescriptor sd;
			sd = (ITransportServiceDescriptor) UuahServer.getUuahContext()
					.getRegistry().lookupServiceDescriptor(
							ServiceDescriptorFactory.PROVIDER_SERVICE_TYPE,
							scheme);
			if (sd == null) {
				throw new ServiceException(ServerMessages
						.noServiceTransportDescriptor(scheme));
			}
			IEndpointURIBuilder builder = sd.createEndpointBuilder();
			IEndpointURI built = builder.build(this.uri);
			initialise(built);
		} catch (Exception e) {
			throw new InitialisationException(e, this);
		}
	}

	private String retrieveSchemeMetaInfo(String url) {
		int i = url.indexOf(':');
		if (i == -1) {
			return null;
		}
		if (url.charAt(i + 1) == '/') {
			return null;
		} else {
			return url.substring(0, i);
		}
	}

	protected boolean validateUrl(String url) {
		return (url.indexOf(":/") > 0);
	}

	private void initialise(IEndpointURI endpointUri) {
		this.address = endpointUri.getAddress();
		if (this.endpointName == null) {
			this.endpointName = endpointUri.getEndpointName();
		}
		this.connectorName = endpointUri.getConnectorName();
		this.requestTransformers = endpointUri.getRequestTransformers();
		this.responseTransformers = endpointUri.getResponseTransformers();
		this.params = endpointUri.getParams();
		this.uri = endpointUri.getUri();
		this.userInfo = endpointUri.getUserInfo();
	}

	public String getAddress() {
		return address;
	}

	public String getEndpointName() {
		return (StringUtils.isEmpty(endpointName) ? null : endpointName);
	}

	public Properties getParams() {
		if (params.size() == 0 && getQuery() != null) {
			params = PropertiesUtils.getPropertiesFromQueryString(getQuery());
		}
		return params;
	}

	public Properties getUserParams() {
		Properties p = new Properties();
		p.putAll(getParams());
		p.remove(PROPERTY_ENDPOINT_NAME);
		p.remove(PROPERTY_ENDPOINT_URI);
		p.remove(PROPERTY_REQUEST_TRANSFORMERS);
		return p;
	}

	public URI parseServerAuthority() throws URISyntaxException {
		return uri.parseServerAuthority();
	}

	public URI normalize() {
		return uri.normalize();
	}

	public URI resolve(URI uri) {
		return uri.resolve(uri);
	}

	public URI resolve(String str) {
		return uri.resolve(str);
	}

	public URI relativize(URI uri) {
		return uri.relativize(uri);
	}

	public String getScheme() {
		return uri.getScheme();
	}

	public String getFullScheme() {
		return (schemeMetaInfo == null ? uri.getScheme() : schemeMetaInfo + ':'
				+ uri.getScheme());

	}

	public boolean isAbsolute() {
		return uri.isAbsolute();
	}

	public boolean isOpaque() {
		return uri.isOpaque();
	}

	public String getRawSchemeSpecificPart() {
		return uri.getRawSchemeSpecificPart();
	}

	public String getSchemeSpecificPart() {
		return uri.getSchemeSpecificPart();
	}

	public String getRawAuthority() {
		return uri.getRawAuthority();
	}

	public String getAuthority() {
		return uri.getAuthority();
	}

	public String getRawUserInfo() {
		return uri.getRawUserInfo();
	}

	public String getUserInfo() {
		return userInfo;
	}

	public String getHost() {
		return uri.getHost();
	}

	public int getPort() {
		return uri.getPort();
	}

	public String getRawPath() {
		return uri.getRawPath();
	}

	public String getPath() {
		return uri.getPath();
	}

	public String getRawQuery() {
		return uri.getRawQuery();
	}

	public String getQuery() {
		return uri.getQuery();
	}

	public String getRawFragment() {
		return uri.getRawFragment();
	}

	public String getFragment() {
		return uri.getFragment();
	}

	public String toString() {
		return uri.toASCIIString();
	}

	public String getRequestTransformers() {
		return requestTransformers;
	}

	public URI getUri() {
		return uri;
	}

	public String getConnectorName() {
		return connectorName;
	}

	public String getSchemeMetaInfo() {
		return (schemeMetaInfo == null ? uri.getScheme() : schemeMetaInfo);
	}

	public String getFilterAddress() {
		return filterAddress;
	}

	public void setEndpointName(String name) {
		endpointName = name;
	}

	public String getUser() {
		if (StringUtils.isNotBlank(userInfo)) {
			int i = userInfo.indexOf(':');
			if (i == -1) {
				return userInfo;
			} else {
				return userInfo.substring(0, i);
			}
		}
		return null;
	}

	public String getResponseTransformers() {
		return responseTransformers;
	}

	public String getPassword() {
		if (StringUtils.isNotBlank(userInfo)) {
			int i = userInfo.indexOf(':');
			if (i > -1) {
				return userInfo.substring(i + 1);
			}
		}
		return null;
	}

	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof UuahEndpointURI)) {
			return false;
		}
		UuahEndpointURI uuahEndpointURI = (UuahEndpointURI) o;
		return ClassUtils.equal(address, uuahEndpointURI.address)
				&& ClassUtils
						.equal(connectorName, uuahEndpointURI.connectorName)
				&& ClassUtils.equal(endpointName, uuahEndpointURI.endpointName)
				&& ClassUtils
						.equal(filterAddress, uuahEndpointURI.filterAddress)
				&& ClassUtils.equal(params, uuahEndpointURI.params)
				&& ClassUtils.equal(schemeMetaInfo,
						uuahEndpointURI.schemeMetaInfo)
				&& ClassUtils.equal(requestTransformers,
						uuahEndpointURI.requestTransformers)
				&& ClassUtils.equal(responseTransformers,
						uuahEndpointURI.responseTransformers)
				&& ClassUtils.equal(uri, uuahEndpointURI.uri);
	}

	public int hashCode() {
		return ClassUtils.hash(new Object[] { address, filterAddress,
				endpointName, connectorName, requestTransformers,
				responseTransformers, params, uri, schemeMetaInfo });
	}
}
